import sys
from copy import copy
from functools import cached_property
from typing import Literal

# noinspection PyUnresolvedReferences
from alibabacloud_ecs20140526.client import Client as EcsClient
from alibabacloud_tea_openapi.models import Config as OpenApiConfig
from paramiko.client import SSHClient
from zkl_serialization import JsonValue

from jobs_agency.basics import JobsStore
from jobs_agency.platforms.cloud import CloudAttachedJob, CloudPlatform
from jobs_agency.platforms.ssh import SSHConfig, SSHPlan
from .config import AlibabaCloudClientConfig, AlibabaCloudEcsConfig
from .utils_ecs import delete_instance, get_instance_ip_or_retry, get_instance_status, run_instance
from .utils_life import make_prepare_func, make_release_script


class AlibabaCloudEcsPlatform(CloudPlatform[str, AlibabaCloudEcsConfig]):
    def __init__(self,
        store: JobsStore,
        acc_config: AlibabaCloudClientConfig,
        network: Literal['public', 'inner'],
    ):
        super().__init__(store)
        self._acc_config = acc_config
        self._network = network

    @cached_property
    def ecs_client(self) -> EcsClient:
        return EcsClient(OpenApiConfig(
            access_key_id=self._acc_config.access_key_id,
            access_key_secret=self._acc_config.access_key_secret,
            region_id=self._acc_config.region_id))

    @property
    def identifier(self) -> JsonValue:
        return {
            **super().identifier,
            'access_key_id': self._acc_config.access_key_id,
            'region_id':     self._acc_config.region_id
        }

    def _allocate_instance(self, config: AlibabaCloudEcsConfig) -> str:
        return run_instance(self.ecs_client, config.ecs_request)

    def _configure_ssh(self, config: AlibabaCloudEcsConfig, instance_id: str) -> SSHConfig:
        instance_ip = get_instance_ip_or_retry(self.ecs_client, instance_id, network=self._network)
        ssh_config = copy(config.ssh)
        ssh_config.hostname = instance_ip
        return ssh_config

    def _configure_plan(self, attached_job: CloudAttachedJob, plan: SSHPlan) -> SSHPlan:
        prepare_func_platform = make_prepare_func(self._acc_config)
        prepare_func_plan = plan.prepare_func

        def prepare_func(ssh_client: SSHClient):
            print("Prepare func (for detached job) from platform...", file=sys.stderr)
            prepare_func_platform(ssh_client)

            if prepare_func_plan:
                print("Prepare func (for detached job) from plan...", file=sys.stderr)
                prepare_func_plan(ssh_client)

            print("Preparation (for detached job) done!", file=sys.stderr)

        release_script_platform = make_release_script(attached_job.instance_id)
        release_script_config = attached_job.release_script
        release_script_plan = plan.release_script

        release_script = ""
        if release_script_plan:
            release_script += "echo 'Release script from plan...' >&2\n"
            release_script += release_script_plan + "\n"
        if release_script_config:
            release_script += "echo 'Release script from config...' >&2\n"
            release_script += release_script_config + "\n"
        release_script += "echo 'Release script from platform...' >&2\n"
        release_script += release_script_platform + "\n"
        release_script += "echo 'Releasing done!' >&2\n"

        plan = copy(plan)
        plan.prepare_func = prepare_func
        plan.release_script = release_script
        return plan

    def _check_instance_running(self, instance_id: str) -> bool:
        state = get_instance_status(self.ecs_client, instance_id)
        return state is not None

    def _release_instance(self, instance_id: str):
        delete_instance(self.ecs_client, instance_id)
